# setup.py

from __future__ import division

import math
import contextlib
import operator

from ot_simulator.ordered_list import ordered_list
from ot_simulator import transform

class setup(object):
    r'''Simulate the OT lathe with ``work_stack``, `tool_stack``.

    Simulate the ``work_stack`` before the ``tool_stack`` iff
    ``work_stack_first`` is True.
    '''
    def __init__(self, name, work_stack, tool_stack, cutting_passes=(),
                 work_stack_first = False, **types_of_runs):
        self.name = name
        self.work_stack = work_stack
        self.tool_stack = tool_stack
        self.work_stack_first = work_stack_first
        self.cutting_passes = list(cutting_passes)
        self.types_of_runs = types_of_runs

    def register(self, params):
        r'''Register all self.work_stack and self.tool_stack components.

        This adds all components to params by each component's name.
        '''
        register_stack(self.tool_stack, params)
        register_stack(self.work_stack, params)

    def run(self, type_of_run, dest_machine):
        p2 = self.types_of_runs[type_of_run].copy()
        self.register(p2)
        self.cutting_passes[0].run(tuple(self.cutting_passes[1:]), self,
                                   dest_machine, p2)

    def cont(self, type_of_run, dest_machine):
        p2 = self.types_of_runs[type_of_run].copy()
        self.register(p2)
        self.cutting_passes[0].cont(tuple(self.cutting_passes[1:]), self,
                                    dest_machine, p2)

    def generate_point(self, params):
        r'''Generates one point at the time and settings defined by ``params``.

        Return the resulting work_to_tool_transform.  The ``params`` are
        modified in place.
        '''
        if self.work_stack_first:
            work_transform = simulate_stack(self.work_stack, params)
            tool_transform = simulate_stack(self.tool_stack, params)
        else:
            tool_transform = simulate_stack(self.tool_stack, params)
            work_transform = simulate_stack(self.work_stack, params)

        # Calculate the transformation from the work to the tool.
        work_to_tool_transform = tool_transform.subtract(work_transform)
        #print "work_to_tool_transform:", work_to_tool_transform

        return work_to_tool_transform

def simulate_stack(stack, params, transform_in=transform.Identity_transform):
    ans = transform_in
    for comp in stack:
        ans = comp.get_transform(params, ans)
    return ans

def register_stack(stack, params):
    for comp in stack: comp.register(params)

